#include "scheme.h"
#include "timer_utility.h"

/* *
 * @brief 构造函数，初始化Scheme对象。
 *
 * @param context 上下文对象，包含加密参数和操作函数。
 */
Scheme::Scheme(Context &context) : context(context) {}

/* *
 * @brief 构造函数，初始化Scheme对象并添加加密和乘法密钥。
 *
 * @param secretKey 秘钥对象，包含加密和乘法所需的密钥。
 * @param context 上下文对象，包含加密参数和操作函数。
 */
Scheme::Scheme(SecretKey &secretKey, Context &context) : context(context)
{
    ex = new uint64_t[context.L << context.logN];
    buffQ = new uint64_t[context.L * context.N];
    buff = new uint64_t[context.L * context.N];
    nttIndex = new uint64_t[context.N];
    sx = new uint64_t[(context.L + context.K) * context.N];

    TimerUtility  TimerUtility ;
    TimerUtility .Start("Gen PublicKey");
    AddEncKey(secretKey);
    TimerUtility .Stop("Gen PublicKey");
    TimerUtility .Start("Gen SwitchKey");
    AddMultKey(secretKey);
    TimerUtility .Stop("Gen SwitchKey");
}

/* *
 * @brief 添加加密密钥到密钥映射中。
 *
 * @param secretKey 秘钥对象，包含加密所需的密钥。
 */
void Scheme::AddEncKey(SecretKey &secretKey)
{
    uint64_t *ax = new uint64_t[context.L << context.logN]();
    uint64_t *bx = new uint64_t[context.L << context.logN]();

    context.SampleUniform(ax, context.L);
    context.SampleGauss(ex, context.L);
    context.NTTAndEqual(ex, context.L);
    // ax*sk
    context.Mul(bx, ax, secretKey.sx, context.L);
    // ns*e
    context.MulConstAndEqual(ex, context.gaussNoise, context.L);
    // bx = ns*e - ax*sk
    context.Sub2AndEqual(ex, bx, context.L);
    encKey = Key(ax, bx);
}

/* *
 * @brief 添加乘法密钥到密钥映射中。
 *
 * @param secretKey 秘钥对象，包含乘法所需的密钥。
 */
void Scheme::AddMultKey(SecretKey &secretKey)
{
    std::copy(secretKey.sx, secretKey.sx + context.L * context.N, buffQ);
    context.MulAndEqual(buffQ, secretKey.sx, context.L);

    int decompRNS = DecompRNS(context.L, context.K);
    int decompPw2 = DecompPw2(context.L, context.K, context.pow2Base, context.qVec, context.pVec);
    vector<vector<uint64_t *>> rax;
    vector<vector<uint64_t *>> rbx;
    rax.resize(decompRNS);
    rbx.resize(decompRNS);
    for (int i = 0; i < decompRNS; i++) {
        rax[i].resize(decompPw2);
        rbx[i].resize(decompPw2);
        for (int k = 0; k < decompPw2; k++) {
            rax[i][k] = new uint64_t[(context.L + context.K) * context.N];
            rbx[i][k] = new uint64_t[(context.L + context.K) * context.N];

            // // ct = (e, 0)
            context.SampleGauss(context.ex, context.L);
            context.ExtendBasisSmallNormAndCenter(context.ex + context.L * context.N, context.ex, context.K);
            context.NTTAndEqual(context.ex, context.L, context.K);

            // ct = (e, a)
            context.SampleUniform(rax[i][k], context.L, context.K);

            // (-a*sk + e, a)
            context.Mul(rbx[i][k], rax[i][k], secretKey.sx, context.L, context.K);
            context.Sub2AndEqual(context.ex, rbx[i][k], context.L, context.K);
        }
    }
    // P * pt
#pragma omp parallel for
    for (int i = 0; i < context.L; i++) {
        context.QiMulConst(buff + i * context.N, buffQ + i * context.N,
            context.pModulusAtLevel[context.K - 1] % context.qVec[i], i);
    }
    int index = 0;
    for (int j = 0; j < decompPw2; j++) {
        for (int i = 0; i < decompRNS; i++) {
            // e + (m * P * w^2j) * (q_star * q_tild) mod QP
            //
            // q_prod = prod(q[i*#Pi+j])
            // q_star = Q/qprod
            // q_tild = q_star^-1 mod q_prod
            //
            // Therefore : (pt * P * w^2j) * (q_star * q_tild) = pt*P*w^2j mod
            // q[i*#Pi+j], else 0
            for (int k = 0; k < context.K; k++) {
                index = i * (context.K) + k;
                // Handle cases where #pj does not divide #qi
                if (index >= context.L) {
                    break;
                }
                uint64_t qi = context.qVec[index];
                uint64_t *p0tmp = buff + index * context.N;
                uint64_t *p1tmp = rbx[i][j] + index * context.N;
                for (int w = 0; w < context.N; w++) {
                    AddModAndEqual(p1tmp[w], p0tmp[w], qi);
                }
            }
        }
        // w^2j
        // ringQ.MulScalar(buff, 1<<logbase2, buff)
    }
    multKey = SwitchKey(rax, rbx);
}

/* *
 * @brief 添加左旋转密钥。
 *
 * @param secretKey 秘钥对象，包含旋转所需的密钥。
 * @param rot 旋转的槽数。
 */
void Scheme::AddLeftRotKey(SecretKey &secretKey, long rot)
{
    uint64_t galElInv = 0;
    // rot = 0 表示 row_rotate
    if (rot == 0) {
        galElInv = context.M - 1;
    } else {
        int e = (-rot) & (context.M - 1);
        galElInv = context.FastPow(5, e, context.M);
    }

    context.PermuteNTTIndex(galElInv, nttIndex);
    context.PermuteNTTWithIndex(sx, secretKey.sx, nttIndex, context.L + context.K);

    int decompRNS = DecompRNS(context.L, context.K);
    int decompPw2 = DecompPw2(context.L, context.K, context.pow2Base, context.qVec, context.pVec);
    vector<vector<uint64_t *>> rax;
    vector<vector<uint64_t *>> rbx;
    rax.resize(decompRNS);
    rbx.resize(decompRNS);
    for (int i = 0; i < decompRNS; i++) {
        rax[i].resize(decompPw2);
        rbx[i].resize(decompPw2);
        for (int k = 0; k < decompPw2; k++) {
            rax[i][k] = new uint64_t[(context.L + context.K) * context.N];
            rbx[i][k] = new uint64_t[(context.L + context.K) * context.N];
            // ct = (e, 0)
            context.SampleGauss(context.ex, context.L);
            context.ExtendBasisSmallNormAndCenter(context.ex + context.L * context.N, context.ex, context.K);
            context.NTTAndEqual(context.ex, context.L, context.K);
            // ct = (e, a)
            context.SampleUniform(rax[i][k], context.L, context.K);
            // (-a*sk + e, a)
            context.Mul(rbx[i][k], rax[i][k], sx, context.L, context.K);
            context.Sub2AndEqual(context.ex, rbx[i][k], context.L, context.K);
        }
    }
    // P * pt
#pragma omp parallel for
    for (int i = 0; i < context.L; i++) {
        context.QiMulConst(buff + i * context.N, secretKey.sx + i * context.N,
            context.pModulusAtLevel[context.K - 1] % context.qVec[i], i);
    }
    int index = 0;
    for (int j = 0; j < decompPw2; j++) {
        for (int i = 0; i < decompRNS; i++) {
            // e + (m * P * w^2j) * (q_star * q_tild) mod QP
            //
            // q_prod = prod(q[i*#Pi+j])
            // q_star = Q/qprod
            // q_tild = q_star^-1 mod q_prod
            //
            // Therefore : (pt * P * w^2j) * (q_star * q_tild) = pt*P*w^2j mod
            // q[i*#Pi+j], else 0
            for (int k = 0; k < context.K; k++) {
                index = i * (context.K) + k;
                // Handle cases where #pj does not divide #qi
                if (index >= context.L) {
                    break;
                }
                uint64_t qi = context.qVec[index];
                uint64_t *p0tmp = buff + index * context.N;
                uint64_t *p1tmp = rbx[i][j] + index * context.N;
                for (int w = 0; w < context.N; w++) {
                    AddModAndEqual(p1tmp[w], p0tmp[w], qi);
                }
            }
        }
        // w^2j
        // ringQ.MulScalar(buff, 1<<logbase2, buff)
    }
    leftRotKeyMap.insert(pair<long, SwitchKey>(rot, SwitchKey(rax, rbx)));
}

/* *
 * @brief 添加所有必要的左旋转密钥。
 *
 * @param secretKey 秘钥对象，包含旋转所需的密钥。
 */
void Scheme::AddLeftRotKeys(SecretKey &secretKey)
{
    for (long i = 0; i < context.logNh; i++) {
        long idx = 1 << i;
        if (leftRotKeyMap.find(idx) == leftRotKeyMap.end()) {
            AddLeftRotKey(secretKey, idx);
        }
    }
}

/* *
 * @brief 添加所有必要的右旋转密钥。
 *
 * @param secretKey 秘钥对象，包含旋转所需的密钥。
 */
void Scheme::AddRightRotKeys(SecretKey &secretKey)
{
    for (long i = 0; i < context.logNh; i++) {
        long idx = context.Nh - (1 << i);
        if (leftRotKeyMap.find(idx) == leftRotKeyMap.end()) {
            AddLeftRotKey(secretKey, idx);
        }
    }
}

/*
 * @brief 使用给定的私钥对明文进行加密，生成密文。
 *
 * @param secretkey： 输入的私钥，用于加密操作。
 * @param message： 输入的明文对象，包含待加密的多项式数据及元数据。
 *
 * @return Ciphertext： 返回加密后的密文对象，包含加密后的多项式数据及元数据。
 *
 * 该函数将输入的明文 `message` 使用私钥 `secretkey` 进行加密，
 * 生成同态加密系统中的密文。加密过程中引入了随机噪声，确保加密的安全性。
 *
 * 实现步骤：
 * 1. 动态分配内存，初始化密文的两个主要部分 `ax` 和 `bx` 及噪声项 `ex`。
 * 2. 生成均匀分布的多项式 `ax`，并与私钥多项式 `sx` 相乘，结果存储在 `bx`。
 * 3. 对 `bx` 取负，并在数论变换 (NTT) 域中进行处理。
 * 4. 生成服从高斯分布的噪声多项式 `ex` 并将其转换到 NTT 域。
 * 5. 将噪声多项式 `ex` 和明文多项式 `mx` 加入到 `bx`，形成加密结果。
 * 6. 返回包含 `ax` 和 `bx` 的密文对象。
 */
Ciphertext Scheme::EncryptMsg(SecretKey &secretkey, Plaintext &message)
{
    uint64_t* axEncKey = new uint64_t[message.l << context.logN]();
    uint64_t* bxEncKey = new uint64_t[message.l << context.logN]();

    context.SampleUniform(axEncKey, message.l);
    context.Mul(axEncKey, secretkey.sx, bxEncKey, message.l);
    context.NegateAndEqual(bxEncKey, message.l);
    context.NTTAndEqual(bxEncKey, message.l);

    context.SampleGauss(ex, message.l);
    context.NTTAndEqual(ex, message.l);

    context.AddAndEqual(bxEncKey, ex, message.l);
    context.AddAndEqual(bxEncKey, message.mx, message.l);

    return Ciphertext(axEncKey, bxEncKey, context.N, message.slots, message.l);
}

/* *
 * @brief 加密明文消息。
 *
 * @param message 明文对象，包含要加密的消息。
 * @return Ciphertext 加密后的密文。
 */
Ciphertext Scheme::EncryptMsg(Plaintext &message)
{
    uint64_t *ax = new uint64_t[message.l << context.logN]();
    uint64_t *bx = new uint64_t[message.l << context.logN]();
    context.SampleZO(context.vx, message.l);
    context.NTTAndEqual(context.vx, message.l);
    // p1 * v
    context.Mul(ax, context.vx, encKey.ax, message.l);
    // e0 * ns
    context.SampleGauss(context.ex, message.l);
    context.NTTAndEqual(context.ex, message.l);
    context.MulConstAndEqual(context.ex, context.gaussNoise, message.l);
    // ax = p1 * v + e0 * ns
    context.AddAndEqual(ax, context.ex, message.l);
    // p0 * v
    context.Mul(bx, context.vx, encKey.bx, message.l);
    // e1 * ns
    context.SampleGauss(context.ex, message.l);
    context.NTTAndEqual(context.ex, message.l);
    context.MulConstAndEqual(context.ex, context.gaussNoise, message.l);
    // bx = p0 * v + e1 * ns
    context.AddAndEqual(bx, context.ex, message.l);
    // bx = bx + m
    context.AddAndEqual(bx, message.mx, message.l);

    return Ciphertext(ax, bx, context.N, message.slots, message.l);
}

/* *
 * @brief 解密密文消息。
 *
 * @param secretKey 秘钥对象，包含解密所需的密钥。
 * @param cipher 密文对象，包含要解密的消息。
 * @return Plaintext 解密后的明文。
 */
Plaintext Scheme::DecryptMsg(SecretKey &secretKey, Ciphertext &cipher)
{
    uint64_t *mx = new uint64_t[context.N * cipher.l]();
    // mx = bx + ax * sx
    context.Mul(mx, cipher.ax, secretKey.sx, cipher.l,0);
    context.AddAndEqual(mx, cipher.bx, cipher.l,0);
    return Plaintext(mx, context.N, cipher.slots, cipher.l);
}

/* *
 * @brief 对密文进行取反操作。
 *
 * @param cipher 密文对象，包含要取反的消息。
 * @return Ciphertext 取反后的密文。
 */
Ciphertext Scheme::Negate(Ciphertext &cipher)
{
    uint64_t *axres = new uint64_t[cipher.l << context.logN];
    uint64_t *bxres = new uint64_t[cipher.l << context.logN];

    context.Negate(axres, cipher.ax, cipher.l);
    context.Negate(bxres, cipher.bx, cipher.l);

    return Ciphertext(axres, bxres, context.N, cipher.slots, cipher.l);
}

/* *
 * @brief 对密文进行取反操作并更新原密文。
 *
 * @param cipher 密文对象，包含要取反的消息。
 */
void Scheme::NegateAndEqual(Ciphertext &cipher)
{
    long shift = 0;
    for (long i = 0; i < cipher.l; i++) {
        context.QiNegateAndEqual(cipher.ax + shift, i);
        context.QiNegateAndEqual(cipher.bx + shift, i);
        shift += context.N;
    }
}

/* *
 * @brief 对两个密文进行相加操作。
 *
 * @param cipher1 第一个密文对象。
 * @param cipher2 第二个密文对象。
 * @return Ciphertext 相加后的密文。
 */
Ciphertext Scheme::Add(Ciphertext &cipher1, Ciphertext &cipher2)
{
    int minLevel = min(cipher1.l, cipher2.l);

    uint64_t *axres = new uint64_t[minLevel << context.logN];
    uint64_t *bxres = new uint64_t[minLevel << context.logN];

    context.Add(axres, cipher1.ax, cipher2.ax, minLevel);
    context.Add(bxres, cipher1.bx, cipher2.bx, minLevel);

    return Ciphertext(axres, bxres, context.N, cipher1.slots, minLevel);
}


/* *
 * @brief 对两个密文进行相加操作并更新第一个密文。
 *
 * @param cipher1 第一个密文对象，将被更新。
 * @param cipher2 第二个密文对象。
 */
void Scheme::AddAndEqual(Ciphertext &cipher1, Ciphertext &cipher2)
{
    cipher1.l = min(cipher1.l, cipher2.l);
    context.AddAndEqual(cipher1.ax, cipher2.ax, cipher1.l);
    context.AddAndEqual(cipher1.bx, cipher2.bx, cipher1.l);
}

/* *
 * @brief 对两个密文进行相减操作。
 *
 * @param cipher1 被减密文对象。
 * @param cipher2 减密文对象。
 * @return Ciphertext 相减后的密文。
 */
Ciphertext Scheme::Sub(Ciphertext &cipher1, Ciphertext &cipher2)
{
    if (cipher1.l != cipher2.l) {
        throw invalid_argument("Ciphertexts are not comparable");
    }

    uint64_t *axres = new uint64_t[cipher1.l << context.logN];
    uint64_t *bxres = new uint64_t[cipher1.l << context.logN];

    context.Sub(axres, cipher1.ax, cipher2.ax, cipher1.l);
    context.Sub(bxres, cipher1.bx, cipher2.bx, cipher1.l);

    return Ciphertext(axres, bxres, context.N, cipher1.slots, cipher1.l);
}

/* *
 * @brief 对两个密文进行相减操作并更新第一个密文。
 *
 * @param cipher1 被减密文对象，将被更新。
 * @param cipher2 减密文对象。
 */
void Scheme::SubAndEqual(Ciphertext &cipher1, Ciphertext &cipher2)
{
    if (cipher1.l != cipher2.l) {
        throw invalid_argument("Ciphertexts are not comparable");
    }

    context.SubAndEqual(cipher1.ax, cipher2.ax, cipher1.l);
    context.SubAndEqual(cipher1.bx, cipher2.bx, cipher1.l);
}

/* *
 * @brief 对两个密文进行第二种相减操作并更新第一个密文。
 *
 * @param cipher1 被减密文对象，将被更新。
 * @param cipher2 减密文对象。
 */
void Scheme::Sub2AndEqual(Ciphertext &cipher1, Ciphertext &cipher2)
{
    if (cipher1.l != cipher2.l) {
        throw invalid_argument("Ciphertexts are not comparable");
    }

    context.Sub2AndEqual(cipher1.ax, cipher2.ax, cipher1.l);
    context.Sub2AndEqual(cipher1.bx, cipher2.bx, cipher1.l);
}


void Scheme::GadgetProductSinglePAndBitDecompNoModDown(int levelQ, int levelP, uint64_t *cx, SwitchKey &gadgetCt,
    uint64_t *tmpAx, uint64_t *tmpBx)
{
    context.INTT(context.cxInvNTT, cx, levelQ);
    #pragma omp parallel for
    for (int u = 0; u < levelQ; u++) {
        context.QiNTT(context.cwNTT + u * context.N, context.cxInvNTT, u);
        uint64_t *tmpAxu = tmpAx + u * context.N;
        uint64_t *tmpBxu = tmpBx + u * context.N;
        context.QiMul(tmpBxu, gadgetCt.rbx[0][0] + u * context.N, context.cwNTT + u * context.N, u);
        context.QiMul(tmpAxu, gadgetCt.rax[0][0] + u * context.N, context.cwNTT + u * context.N, u);
    }
    context.PiNTT(context.cwNTT, context.cxInvNTT, 0);
    uint64_t *tmpAxu = tmpAx + levelQ * context.N;
    uint64_t *tmpBxu = tmpBx + levelQ * context.N;
    context.PiMul(tmpAxu, gadgetCt.rax[0][0] + levelQ * context.N, context.cwNTT, 0);
    context.PiMul(tmpBxu, gadgetCt.rbx[0][0] + levelQ * context.N, context.cwNTT, 0);

    for (int i = 1; i < levelQ; i++) {
    #pragma omp parallel for
        for (int u = 0; u < levelQ; u++) {
            for (uint64_t m = 0; m < context.N; m++) {
                context.cwQ[u * context.N + m] = context.cxInvNTT[i * context.N + m];
            }
            context.QiNTT(context.cwNTT + u * context.N, context.cwQ + u * context.N, u);
            context.QiMul(context.polAx + u * context.N, gadgetCt.rax[i][0] + u * context.N, context.cwNTT + u * context.N, u);
            context.QiMul(context.polBx + u * context.N, gadgetCt.rbx[i][0] + u * context.N, context.cwNTT + u * context.N, u);
            context.QiAddAndEqual(tmpAx + u * context.N, context.polAx + u * context.N, u);
            context.QiAddAndEqual(tmpBx + u * context.N, context.polBx + u * context.N, u);
        }
        context.PiNTT(context.cwNTT, context.cxInvNTT + i * context.N, 0);
        context.PiMul(context.polAx, gadgetCt.rax[i][0] + levelQ * context.N, context.cwNTT, 0);
        context.PiMul(context.polBx, gadgetCt.rbx[i][0] + levelQ * context.N, context.cwNTT, 0);
        context.PiAddAndEqual(tmpAx + levelQ * context.N, context.polAx, 0);
        context.PiAddAndEqual(tmpBx + levelQ * context.N, context.polBx, 0);
    }
}



/* *
 * @brief 对两个密文进行相乘操作。
 *
 * @param cipher1 第一个密文对象。
 * @param cipher2 第二个密文对象。
 * @param res 相乘后的密文。
 */

void Scheme::Mult(Ciphertext &res, Ciphertext &cipher1, Ciphertext &cipher2)
{
    int minLevel = min(cipher1.l, cipher2.l);
    context.MulConst(context.axt, cipher1.ax, context.tVec[0], minLevel);
    context.Mul(context.axax, context.axt, cipher2.ax, minLevel);
    context.MulConst(context.bxt, cipher1.bx, context.tVec[0], minLevel);
    context.Mul(context.bxbx, context.bxt, cipher2.bx, minLevel);
    context.Add(context.axbx1, context.axt, context.bxt, minLevel);
    context.Add(context.axbx2, cipher2.ax, cipher2.bx, minLevel);
    context.MulAndEqual(context.axbx1, context.axbx2, minLevel);
    context.GadgetProduct(minLevel, context.K, context.axax, multKey, context.tmpCtAx, context.tmpCtBx);
    context.Add(res.bx, context.bxbx, context.tmpCtBx, minLevel);
    context.Add(res.ax, context.axbx1, context.tmpCtAx, minLevel);
    context.SubAndEqual(res.ax, context.axax, minLevel);
    context.SubAndEqual(res.ax, context.bxbx, minLevel);
}

/* *
 * @brief 对两个密文进行相乘操作。
 *
 * @param cipher1 第一个密文对象。
 * @param cipher2 第二个密文对象。
 * @res Ciphertext 返回结果，相乘后的密文。
 */
Ciphertext Scheme::Mult(Ciphertext &cipher1, Ciphertext &cipher2)
{
    int minLevel = min(cipher1.l, cipher2.l);
    uint64_t *axmult = new uint64_t[minLevel << context.logN];
    uint64_t *bxmult = new uint64_t[minLevel << context.logN];
    context.MulConst(context.axt, cipher1.ax, context.tVec[0], minLevel);
    context.Mul(context.axax, context.axt, cipher2.ax, minLevel);
    context.MulConst(context.bxt, cipher1.bx, context.tVec[0], minLevel);
    context.Mul(context.bxbx, context.bxt, cipher2.bx, minLevel);
    context.Add(context.axbx1, context.axt, context.bxt, minLevel);
    context.Add(context.axbx2, cipher2.ax, cipher2.bx, minLevel);
    context.MulAndEqual(context.axbx1, context.axbx2, minLevel);
    context.GadgetProduct(minLevel, context.K, context.axax, multKey, context.tmpCtAx, context.tmpCtBx);
    context.Add(bxmult, context.bxbx, context.tmpCtBx, minLevel);
    context.Add(axmult, context.axbx1, context.tmpCtAx, minLevel);
    context.SubAndEqual(axmult, context.axax, minLevel);
    context.SubAndEqual(axmult, context.bxbx, minLevel);
    return Ciphertext(axmult, bxmult, context.N, cipher1.slots, minLevel);
}

/* *
 * @brief 密文乘常数操作。
 *
 * @param cipher 密文对象，将被更新。
 * @param cnst 被乘常数。
 */
void Scheme::MultConstAndEqual(Ciphertext &cipher, double cnst)
{
    uint64_t tmpr = abs(cnst);
    context.MulConstAndEqual(cipher.ax, tmpr, cipher.l);
    context.MulConstAndEqual(cipher.bx, tmpr, cipher.l);

    if (cnst < 0) {
        context.NegateAndEqual(cipher.ax, cipher.l);
        context.NegateAndEqual(cipher.bx, cipher.l);
    }
}


/* *
 * @brief 快速左旋转并更新密文。
 *
 * @param cipher 密文对象，将被更新。
 * @param rotSlots 旋转的槽数。
 */
void Scheme::LeftRotateAndEqualFast(Ciphertext &cipher, long rotSlots)
{
    uint64_t galElInv = 0;
    if (rotSlots == 0) {
        galElInv = context.M - 1;
    } else {
        galElInv = context.FastPow(5, rotSlots, context.M);
    }
    SwitchKey swtichkey = leftRotKeyMap.at(rotSlots);
    context.GadgetProduct(cipher.l, context.K, cipher.ax, swtichkey, context.tmpCtAx, context.tmpCtBx);
    context.AddAndEqual(context.tmpCtBx, cipher.bx, cipher.l);
    context.PermuteNTTIndex(galElInv, nttIndex);
    context.PermuteNTTWithIndex(cipher.bx, context.tmpCtBx, nttIndex, cipher.l);
    context.PermuteNTTWithIndex(cipher.ax, context.tmpCtAx, nttIndex, cipher.l);
}

/* *
 * @brief 按2的幂次进行左旋转并更新密文。
 *
 * @param cipher 密文对象，将被更新。
 * @param logRotSlots 旋转槽数的对数。
 */
void Scheme::LeftRotateByPo2AndEqual(Ciphertext &cipher, long logRotSlots)
{
    long rotSlots = (1 << logRotSlots);
    LeftRotateAndEqualFast(cipher, rotSlots);
}

/* *
 * @brief 按2的幂次进行右旋转并更新密文。
 *
 * @param cipher 密文对象，将被更新。
 * @param logRotSlots 旋转槽数的对数。
 */
void Scheme::RightRotateByPo2AndEqual(Ciphertext &cipher, long logRotSlots)
{
    long rotSlots = context.Nh - (1 << logRotSlots);
    LeftRotateAndEqualFast(cipher, rotSlots);
}

/* *
 * @brief 对密文进行左旋转操作并返回新的密文。
 *
 * @param cipher 密文对象。
 * @param rotSlots 旋转的槽数。
 * @return Ciphertext 旋转后的新密文。
 */
Ciphertext Scheme::LeftRotate(Ciphertext &cipher, long rotSlots)
{
    Ciphertext res = cipher;
    LeftRotateAndEqual(res, rotSlots);
    return res;
}

/* *
 * @brief 对密文进行左旋转操作并更新原密文。
 *
 * @param cipher 密文对象，将被更新。
 * @param rotSlots 旋转的槽数。
 */
void Scheme::LeftRotateAndEqual(Ciphertext &cipher, long rotSlots)
{
    LeftRotateAndEqualFast(cipher, rotSlots);
}

/* *
 * @brief 对密文进行右旋转操作并返回新的密文。
 *
 * @param cipher 密文对象。
 * @param rotSlots 旋转的槽数。
 * @return Ciphertext 旋转后的新密文。
 */
Ciphertext Scheme::RightRotate(Ciphertext &cipher, long rotSlots)
{
    Ciphertext res = cipher;
    RightRotateAndEqual(res, rotSlots);
    return res;
}

/* *
 * @brief 对密文进行右旋转操作并更新原密文。
 *
 * @param cipher 密文对象，将被更新。
 * @param rotSlots 旋转的槽数。
 */
void Scheme::RightRotateAndEqual(Ciphertext &cipher, long rotSlots)
{
    LeftRotateAndEqualFast(cipher, context.Nh - rotSlots);
}

/* *
 * @brief 将明文与密文相乘。
 *
 * @param cipher 密文对象。
 * @param plaintext 明文对象。
 * @return Ciphertext 乘积后的密文。
 */
Ciphertext Scheme::MultPt(Ciphertext &cipher, Plaintext &plaintext)
{
    uint64_t *axmult = new uint64_t[cipher.l << context.logN]();
    uint64_t *bxmult = new uint64_t[cipher.l << context.logN]();
    context.MulConst(axmult, cipher.ax, context.tVec[0], cipher.l);
    context.MulConst(bxmult, cipher.bx, context.tVec[0], cipher.l);
    context.Mul(axmult, axmult, plaintext.mx, cipher.l);
    context.Mul(bxmult, bxmult, plaintext.mx, cipher.l);
    return Ciphertext(axmult, bxmult, context.N, cipher.slots, cipher.l);
}

/* *
 * @brief 将常数明文添加到密文。
 *
 * @param cipher 密文对象。
 * @param plaintext 明文对象，包含要添加的常数。
 * @return Ciphertext 添加后的密文。
 */
Ciphertext Scheme::AddPt(Ciphertext &cipher, Plaintext &plaintext)
{
    uint64_t *bxadd = new uint64_t[cipher.l << context.logN]();
    context.Add(bxadd, cipher.bx, plaintext.mx, cipher.l);
    return Ciphertext(cipher.ax, bxadd, context.N, cipher.slots, cipher.l);
}

void Scheme::AddPt(Ciphertext &res, Ciphertext &cipher, Plaintext &plaintext)
{
    context.Add(res.bx, cipher.bx, plaintext.mx, cipher.l);
}

/* *
 * @brief 用私钥加密输入的数值数组。
 *
 * @param vals 输入的数值数组。
 * @param slots 槽数，表示可以并行加密的元素数量。
 * @param l 加密的层数。
 * @return Ciphertext 加密后的密文。
 */
Ciphertext Scheme::EncryptBySk(uint64_t *vals, long slots, long l, SecretKey &secretkey)
{
    Plaintext msg = Encode(vals, slots, l);
    return EncryptMsg(secretkey, msg);
}

/* *
 * @brief 加密输入的数值数组。
 *
 * @param vals 输入的数值数组。
 * @param slots 槽数，表示可以并行加密的元素数量。
 * @param l 加密的层数。
 * @return Ciphertext 加密后的密文。
 */
Ciphertext Scheme::Encrypt(uint64_t *vals, long slots, long l)
{
    Plaintext msg = Encode(vals, slots, l);
    return EncryptMsg(msg);
}

/* *
 * @brief 编码明文数值数组。
 *
 * @param v 输入的数值数组。
 * @param slots 槽数，表示可以并行加密的元素数量。
 * @param l 编码的层数。
 * @return Plaintext 编码后的明文对象。
 */
Plaintext Scheme::Encode(uint64_t *v, long slots, long l)
{
    uint64_t *m = new uint64_t[l << context.logN]();
    context.Encode(m, v, slots, l);
    return Plaintext(m, context.N, slots, l);
}

/* *
 * @brief 解密密文并返回数值数组。
 *
 * @param secretKey 秘钥对象，包含解密所需的密钥。
 * @param cipher 密文对象，包含要解密的消息。
 * @return uint64_t* 解密后的数值数组。
 */
uint64_t *Scheme::Decrypt(SecretKey &secretKey, Ciphertext &cipher)
{
    Plaintext msg = DecryptMsg(secretKey, cipher);
    return Decode(msg);
}

/* *
 * @brief 解码明文数值数组。
 *
 * @param msg 解密后的明文对象。
 * @return uint64_t* 解码后的数值数组。
 */
uint64_t *Scheme::Decode(Plaintext &msg)
{
    uint64_t *res = new uint64_t[context.N];
    context.Decode(msg.mx, res, msg.l);
    return res;
}